#!/usr/bin/python
# @Time  : 2020/9/17 16:13
# @Author: LOUIE
# @Desc  : 数据解析类
import ast
import re
from utils.exceptions import NotFoundError

builtin_str = str
basestring = (str, bytes)
numeric_types = (int, float)

# variable_regex = r"\{([\w_]+)\}"     # 变量匹配正则表达式
# function_regex = r"\{{([\w_]+\([\$\w\.\-/_ =,]*\))\}}"     # 方法匹配正则表达式

variable_regex = r"\$([\w_]+)"    # 变量匹配正则表达式
function_regex = r"\$\{([\w_]+\([\$\w\.\-/_ =,]*\))\}"   # 方法匹配正则表达式
function_regex_compile = re.compile(r"^([\w_]+)\(([\$\w\.\-/_ =,]*)\)$")


def parse_string_value(str_value):
    """ 将字符串解析为数字
    e.g. "123" => 123
         "12.2" => 12.3
         "abc" => "abc"
    """
    try:
        return ast.literal_eval(str_value)
    except ValueError:
        return str_value
    except SyntaxError:
        return str_value


def extract_variables(content):
    """
    从内容中提取所有变量名，格式为{{variable}}
    :param content: 数据
    :return:
    """
    try:
        return re.findall(variable_regex, content)
    except TypeError:
        return []


def extract_functions(content):
    """
    从字符串内容中提取所有函数，格式为{{fun()}}
    :param content: 数据
    :return:
    """
    try:
        return re.findall(function_regex, content)
    except TypeError:
        return []


def parse_function(content):
    """
    从字符串内容中解析函数名和参数。
    :param content: 数据文本
    :return:
    """
    matched = function_regex_compile.match(content)
    if not matched:
        raise ValueError("{} not found!".format(content))

    function_meta = {
        "func_name": matched.group(1),
        "args": [],
        "kwargs": {}
    }

    args_str = matched.group(2).strip()
    if args_str == "":
        return function_meta

    args_list = args_str.split(',')
    for arg in args_list:
        arg = arg.strip()
        if '=' in arg:
            key, value = arg.split('=')
            function_meta["kwargs"][key.strip()] = parse_string_value(value.strip())
        else:
            function_meta["args"].append(parse_string_value(arg))

    return function_meta


def get_mapping_variable(variable_name, variables_pool):
    """
    从variables_pool中获取变量。
    :param variable_name: 变量名
    :param variables_pool: 变量映射集合
    :return:
    """
    if variable_name in variables_pool:
        return variables_pool[variable_name]
    else:
        raise NotFoundError(f"{variable_name} is not found.")


def get_mapping_function(function_name, functions_mapping):
    """
    从functions_mapping中获取函数
    :param function_name: 方法名
    :param functions_mapping: 方法映射集合
    :return:
    """
    if function_name in functions_mapping:
        return functions_mapping[function_name]

    try:
        item_func = eval(function_name)
        if callable(item_func):
            return item_func
    except (NameError, TypeError):
        raise ValueError(f"{function_name} is not found.")


def parse_string_functions(content, variables_pool, functions_mapping):
    """用函数映射解析字符串内容"""
    functions_list = extract_functions(content)
    for func_content in functions_list:
        func_meta = parse_function(func_content)

        func_name = func_meta["func_name"]
        args = func_meta.get("args", [])
        kwargs = func_meta.get("kwargs", {})
        args = parse_data(args, variables_pool, functions_mapping)
        kwargs = parse_data(kwargs, variables_pool, functions_mapping)

        func = get_mapping_function(func_name, functions_mapping)
        eval_value = func(*args, **kwargs)
        # temp = "{{" + func_content + "}}"
        temp = "${" + func_content + "}"
        func_content = temp
        if func_content == content:
            content = eval_value
        else:
            content = content.replace(func_content, str(eval_value), 1)

    return content


def parse_string_variables(content: str, variables_pool):
    """
    使用变量映射解析字符串内容。
    如果替换正则需要更换这里的等式
    :param content:
    :param variables_pool:
    :return:
    """
    variables_list = extract_variables(content)
    for variable_name in variables_list:
        variable_value = get_mapping_variable(variable_name, variables_pool)
        temp = f"${variable_name}"
        # temp = "{{" + variable_name + "}}"
        if temp == content:
            content = variable_value
        else:
            if not isinstance(variable_value, str):
                variable_value = builtin_str(variable_value)
            content = content.replace(temp, variable_value, 1)

    return content


def parse_data(content, variables_pool=None, functions_mapping=None):
    """使用变量映射解析数据"""
    if content is None or isinstance(content, (numeric_types, bool, type)):
        return content

    if isinstance(content, (list, set, tuple)):
        return [
            parse_data(item, variables_pool, functions_mapping)
            for item in content
        ]

    if isinstance(content, dict):
        parsed_content = {}
        for key, value in content.items():
            parsed_key = parse_data(key, variables_pool, functions_mapping)
            parsed_value = parse_data(value, variables_pool, functions_mapping)
            parsed_content[parsed_key] = parsed_value

        return parsed_content

    if isinstance(content, basestring):
        variables_pool = variables_pool or {}
        functions_mapping = functions_mapping or {}
        content = content.strip()

        content = parse_string_functions(content, variables_pool, functions_mapping)
        content = parse_string_variables(content, variables_pool)

    return content


if __name__ == '__main__':
    content = {
        "password": "$password",
        "phone": "$staffMobile",
        "verifyCode": "{{verifyCode}}"
    }
    variables_pool = {
        "staffMobile": 13347335633,
        "password": 123456,
        "verifyCode": 820424
    }

    l = parse_data(content, variables_pool)
    print(l)
    print(extract_variables(str(content)))
